From 88a3c2daaeaa36c6474bd3ce745fb0b127875dac Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Tue, 12 Oct 2010 21:48:23 +0200 Subject: [PATCH] GtkGradient: Handle symbolic gradients. The css parser has been modified to parse correctly radial gradients: background-image: -gtk-gradient (radial, center center, 0, center center, 0.8, from (#000), to (#fff)); The theming engine has been modified to correctly animate these, as well as transitions between different pattern types. --- gtk/gtkcssprovider.c | 232 +++++++++++++++++++++++------------------ gtk/gtksymboliccolor.c | 39 ++++++- gtk/gtksymboliccolor.h | 6 ++ gtk/gtkthemingengine.c | 150 +++++++++++++++++--------- 4 files changed, 272 insertions(+), 155 deletions(-) diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c index 598b1ffbee..1150b08d63 100644 --- a/gtk/gtkcssprovider.c +++ b/gtk/gtkcssprovider.c @@ -1354,6 +1354,9 @@ gradient_parse_str (const gchar *str, gchar **end_ptr) { GtkGradient *gradient = NULL; + gdouble coords[6]; + gchar *end; + guint i; if (g_str_has_prefix (str, "-gtk-gradient")) { @@ -1390,15 +1393,67 @@ gradient_parse_str (const gchar *str, SKIP_SPACES (str); - if (type == CAIRO_PATTERN_TYPE_LINEAR) + /* Parse start/stop position parameters */ + for (i = 0; i < 2; i++) { - gdouble coords[4]; - gchar *end; - guint i; + if (*str != ',') + { + *end_ptr = (gchar *) str; + return NULL; + } + + str++; + SKIP_SPACES (str); + + if (strncmp (str, "left", 4) == 0) + { + coords[i * 3] = 0; + str += strlen ("left"); + } + else if (strncmp (str, "right", 5) == 0) + { + coords[i * 3] = 1; + str += strlen ("right"); + } + else if (strncmp (str, "center", 6) == 0) + { + coords[i * 3] = 0.5; + str += strlen ("center"); + } + else + { + coords[i * 3] = g_ascii_strtod (str, &end); + str = end; + } + + SKIP_SPACES (str); + + if (strncmp (str, "top", 3) == 0) + { + coords[(i * 3) + 1] = 0; + str += strlen ("top"); + } + else if (strncmp (str, "bottom", 6) == 0) + { + coords[(i * 3) + 1] = 1; + str += strlen ("bottom"); + } + else if (strncmp (str, "center", 6) == 0) + { + coords[(i * 3) + 1] = 0.5; + str += strlen ("center"); + } + else + { + coords[(i * 3) + 1] = g_ascii_strtod (str, &end); + str = end; + } + + SKIP_SPACES (str); - /* Parse start/stop position parameters */ - for (i = 0; i < 2; i++) + if (type == CAIRO_PATTERN_TYPE_RADIAL) { + /* Parse radius */ if (*str != ',') { *end_ptr = (gchar *) str; @@ -1408,109 +1463,63 @@ gradient_parse_str (const gchar *str, str++; SKIP_SPACES (str); - if (g_str_has_prefix (str, "left")) - { - coords[i * 2] = 0; - str += strlen ("left"); - } - else if (g_str_has_prefix (str, "right")) - { - coords[i * 2] = 1; - str += strlen ("right"); - } - else - { - coords[i * 2] = g_ascii_strtod (str, &end); - str = end; - } + coords[(i * 3) + 2] = g_ascii_strtod (str, &end); + str = end; SKIP_SPACES (str); + } + } - if (g_str_has_prefix (str, "top")) - { - coords[(i * 2) + 1] = 0; - str += strlen ("top"); - } - else if (g_str_has_prefix (str, "bottom")) - { - coords[(i * 2) + 1] = 1; - str += strlen ("bottom"); - } - else - { - coords[(i * 2) + 1] = g_ascii_strtod (str, &end); - str = end; - } + if (type == CAIRO_PATTERN_TYPE_LINEAR) + gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]); + else + gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2], + coords[3], coords[4], coords[5]); - SKIP_SPACES (str); + while (*str == ',') + { + GtkSymbolicColor *color; + gdouble position; + + if (*str != ',') + { + *end_ptr = (gchar *) str; + return gradient; } - gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[2], coords[3]); + str++; + SKIP_SPACES (str); - while (*str == ',') + if (g_str_has_prefix (str, "from")) { - GtkSymbolicColor *color; - gdouble position; + position = 0; + str += strlen ("from"); + SKIP_SPACES (str); - if (*str != ',') + if (*str != '(') { *end_ptr = (gchar *) str; return gradient; } - - str++; + } + else if (g_str_has_prefix (str, "to")) + { + position = 1; + str += strlen ("to"); SKIP_SPACES (str); - if (g_str_has_prefix (str, "from")) + if (*str != '(') { - position = 0; - str += strlen ("from"); - SKIP_SPACES (str); - - if (*str != '(') - { - *end_ptr = (gchar *) str; - return gradient; - } - } - else if (g_str_has_prefix (str, "to")) - { - position = 1; - str += strlen ("to"); - SKIP_SPACES (str); - - if (*str != '(') - { - *end_ptr = (gchar *) str; - return gradient; - } + *end_ptr = (gchar *) str; + return gradient; } - else if (g_str_has_prefix (str, "color-stop")) - { - str += strlen ("color-stop"); - SKIP_SPACES (str); - - if (*str != '(') - { - *end_ptr = (gchar *) str; - return gradient; - } - - str++; - SKIP_SPACES (str); - - position = g_strtod (str, &end); - - str = end; - SKIP_SPACES (str); + } + else if (g_str_has_prefix (str, "color-stop")) + { + str += strlen ("color-stop"); + SKIP_SPACES (str); - if (*str != ',') - { - *end_ptr = (gchar *) str; - return gradient; - } - } - else + if (*str != '(') { *end_ptr = (gchar *) str; return gradient; @@ -1519,26 +1528,30 @@ gradient_parse_str (const gchar *str, str++; SKIP_SPACES (str); - color = symbolic_color_parse_str (str, &end); + position = g_strtod (str, &end); str = end; SKIP_SPACES (str); - if (*str != ')') + if (*str != ',') { *end_ptr = (gchar *) str; return gradient; } + } + else + { + *end_ptr = (gchar *) str; + return gradient; + } - str++; - SKIP_SPACES (str); + str++; + SKIP_SPACES (str); - if (color) - { - gtk_gradient_add_color_stop (gradient, position, color); - gtk_symbolic_color_unref (color); - } - } + color = symbolic_color_parse_str (str, &end); + + str = end; + SKIP_SPACES (str); if (*str != ')') { @@ -1547,7 +1560,22 @@ gradient_parse_str (const gchar *str, } str++; + SKIP_SPACES (str); + + if (color) + { + gtk_gradient_add_color_stop (gradient, position, color); + gtk_symbolic_color_unref (color); + } } + + if (*str != ')') + { + *end_ptr = (gchar *) str; + return gradient; + } + + str++; } *end_ptr = (gchar *) str; @@ -2328,8 +2356,6 @@ gtk_css_provider_load_from_path (GtkCssProvider *css_provider, const gchar *path, GError **error) { - GtkCssProviderPrivate *priv; - g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE); g_return_val_if_fail (path != NULL, FALSE); diff --git a/gtk/gtksymboliccolor.c b/gtk/gtksymboliccolor.c index 310e211342..657ac82e3d 100644 --- a/gtk/gtksymboliccolor.c +++ b/gtk/gtksymboliccolor.c @@ -74,6 +74,8 @@ struct GtkGradient gdouble y0; gdouble x1; gdouble y1; + gdouble radius0; + gdouble radius1; GArray *stops; @@ -269,6 +271,33 @@ gtk_gradient_new_linear (gdouble x0, gradient->y0 = y0; gradient->x1 = x1; gradient->y1 = y1; + gradient->radius0 = 0; + gradient->radius1 = 0; + + gradient->ref_count = 1; + + return gradient; +} + +GtkGradient * +gtk_gradient_new_radial (gdouble x0, + gdouble y0, + gdouble radius0, + gdouble x1, + gdouble y1, + gdouble radius1) +{ + GtkGradient *gradient; + + gradient = g_slice_new (GtkGradient); + gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop)); + + gradient->x0 = x0; + gradient->y0 = y0; + gradient->x1 = x1; + gradient->y1 = y1; + gradient->radius0 = radius0; + gradient->radius1 = radius1; gradient->ref_count = 1; @@ -336,8 +365,14 @@ gtk_gradient_resolve (GtkGradient *gradient, g_return_val_if_fail (GTK_IS_STYLE_SET (style_set), FALSE); g_return_val_if_fail (resolved_gradient != NULL, FALSE); - pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0, - gradient->x1, gradient->y1); + if (gradient->radius0 == 0 && gradient->radius1 == 0) + pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0, + gradient->x1, gradient->y1); + else + pattern = cairo_pattern_create_radial (gradient->x0, gradient->y0, + gradient->radius0, + gradient->x1, gradient->y1, + gradient->radius1); for (i = 0; i < gradient->stops->len; i++) { diff --git a/gtk/gtksymboliccolor.h b/gtk/gtksymboliccolor.h index aedd2aa28e..de403a325f 100644 --- a/gtk/gtksymboliccolor.h +++ b/gtk/gtksymboliccolor.h @@ -49,6 +49,12 @@ GtkGradient * gtk_gradient_new_linear (gdouble x0, gdouble y0, gdouble x1, gdouble y1); +GtkGradient * gtk_gradient_new_radial (gdouble x0, + gdouble y0, + gdouble radius0, + gdouble x1, + gdouble y1, + gdouble radius1); void gtk_gradient_add_color_stop (GtkGradient *gradient, gdouble offset, diff --git a/gtk/gtkthemingengine.c b/gtk/gtkthemingengine.c index ee7e641d82..95536ab09c 100644 --- a/gtk/gtkthemingengine.c +++ b/gtk/gtkthemingengine.c @@ -632,8 +632,6 @@ gtk_theming_engine_render_option (GtkThemingEngine *engine, gint exterior_size, interior_size, pad, thickness; gdouble radius; - /* FIXME: set clipping */ - flags = gtk_theming_engine_get_state (engine); path = gtk_theming_engine_get_path (engine); radius = MIN (width, height) / 2 - 0.5; @@ -897,7 +895,7 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine, cairo_pattern_t *pattern; GtkStateFlags flags; gboolean prelight; - gdouble progress; + gdouble progress, alpha = 1; flags = gtk_theming_engine_get_state (engine); cairo_save (cr); @@ -919,6 +917,9 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine, prelight = gtk_theming_engine_is_state_set (engine, GTK_STATE_PRELIGHT, &progress); + cairo_translate (cr, x, y); + cairo_scale (cr, width, height); + if (prelight || progress > 0 ) { cairo_pattern_t *other_pattern; @@ -942,53 +943,89 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine, if (pattern && other_pattern) { - gdouble offset0, red0, green0, blue0, alpha0; - gdouble offset1, red1, green1, blue1, alpha1; - gdouble x00, x01, y00, y01, x10, x11, y10, y11; + cairo_pattern_type_t type, other_type; gint n0, n1; - guint i; - - cairo_pattern_get_linear_points (pattern, &x00, &y00, &x01, &y01); - cairo_pattern_get_linear_points (other_pattern, &x10, &y10, &x11, &y11); - - new_pattern = cairo_pattern_create_linear (x00 + (x10 - x00) * progress, - y00 + (y10 - y00) * progress, - x01 + (x11 - x01) * progress, - y01 + (y11 - y01) * progress); - cairo_pattern_set_filter (new_pattern, CAIRO_FILTER_FAST); cairo_pattern_get_color_stop_count (pattern, &n0); cairo_pattern_get_color_stop_count (other_pattern, &n1); - i = 0; + type = cairo_pattern_get_type (pattern); + other_type = cairo_pattern_get_type (other_pattern); - /* Blend both gradients into one */ - while (i < n0 && i < n1) + if (type == other_type && n0 == n1) { - cairo_pattern_get_color_stop_rgba (pattern, i, - &offset0, - &red0, &green0, &blue0, - &alpha0); - cairo_pattern_get_color_stop_rgba (other_pattern, i, - &offset1, - &red1, &green1, &blue1, - &alpha1); - - cairo_pattern_add_color_stop_rgba (new_pattern, - offset0 + ((offset1 - offset0) * progress), - red0 + ((red1 - red0) * progress), - green0 + ((green1 - green0) * progress), - blue0 + ((blue1 - blue0) * progress), - alpha0 + ((alpha1 - alpha0) * progress)); - i++; + gdouble offset0, red0, green0, blue0, alpha0; + gdouble offset1, red1, green1, blue1, alpha1; + gdouble x00, x01, y00, y01, x10, x11, y10, y11; + gdouble r00, r01, r10, r11; + guint i; + + if (type == CAIRO_PATTERN_TYPE_LINEAR) + { + cairo_pattern_get_linear_points (pattern, &x00, &y00, &x01, &y01); + cairo_pattern_get_linear_points (other_pattern, &x10, &y10, &x11, &y11); + + new_pattern = cairo_pattern_create_linear (x00 + (x10 - x00) * progress, + y00 + (y10 - y00) * progress, + x01 + (x11 - x01) * progress, + y01 + (y11 - y01) * progress); + } + else + { + cairo_pattern_get_radial_circles (pattern, &x00, &y00, &r00, &x01, &y01, &r01); + cairo_pattern_get_radial_circles (other_pattern, &x10, &y10, &r10, &x11, &y11, &r11); + + new_pattern = cairo_pattern_create_radial (x00 + (x10 - x00) * progress, + y00 + (y10 - y00) * progress, + r00 + (r10 - r00) * progress, + x01 + (x11 - x01) * progress, + y01 + (y11 - y01) * progress, + r01 + (r11 - r01) * progress); + } + + cairo_pattern_set_filter (new_pattern, CAIRO_FILTER_FAST); + i = 0; + + /* Blend both gradients into one */ + while (i < n0 && i < n1) + { + cairo_pattern_get_color_stop_rgba (pattern, i, + &offset0, + &red0, &green0, &blue0, + &alpha0); + cairo_pattern_get_color_stop_rgba (other_pattern, i, + &offset1, + &red1, &green1, &blue1, + &alpha1); + + cairo_pattern_add_color_stop_rgba (new_pattern, + offset0 + ((offset1 - offset0) * progress), + red0 + ((red1 - red0) * progress), + green0 + ((green1 - green0) * progress), + blue0 + ((blue1 - blue0) * progress), + alpha0 + ((alpha1 - alpha0) * progress)); + i++; + } + } + else + { + /* Different pattern types, or different color + * stop counts, alpha blend both patterns. + */ + cairo_rectangle (cr, 0, 0, 1, 1); + cairo_set_source (cr, other_pattern); + cairo_fill (cr); + + /* Set alpha for posterior drawing + * of the target pattern + */ + alpha = 1 - progress; } - - /* FIXME: Handle remaining color stops in both source patterns */ } else if (pattern || other_pattern) { cairo_pattern_t *p; GdkColor *c; - gdouble x0, y0, x1, y1; + gdouble x0, y0, x1, y1, r0, r1; gdouble red0, green0, blue0; gdouble red1, green1, blue1; gint n, i; @@ -1006,8 +1043,17 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine, c = gtk_theming_engine_has_class (engine, "entry") ? base_color : bg_color; } - cairo_pattern_get_linear_points (p, &x0, &y0, &x1, &y1); - new_pattern = cairo_pattern_create_linear (x0, y0, x1, y1); + if (cairo_pattern_get_type (p) == CAIRO_PATTERN_TYPE_LINEAR) + { + cairo_pattern_get_linear_points (p, &x0, &y0, &x1, &y1); + new_pattern = cairo_pattern_create_linear (x0, y0, x1, y1); + } + else + { + cairo_pattern_get_radial_circles (p, &x0, &y0, &r0, &x1, &y1, &r1); + new_pattern = cairo_pattern_create_radial (x0, y0, r0, x1, y1, r1); + } + cairo_pattern_get_color_stop_count (p, &n); red0 = c->red / 65535.; @@ -1067,14 +1113,11 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine, gdk_color_free (other_base); } + cairo_rectangle (cr, 0, 0, 1, 1); + if (pattern) { - cairo_translate (cr, x, y); - cairo_scale (cr, width, height); - - cairo_rectangle (cr, 0, 0, 1, 1); cairo_set_source (cr, pattern); - cairo_pattern_destroy (pattern); } else @@ -1083,8 +1126,6 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine, gdk_cairo_set_source_color (cr, base_color); else gdk_cairo_set_source_color (cr, bg_color); - - cairo_rectangle (cr, x, y, width, height); } if (gtk_theming_engine_has_class (engine, "tooltip")) @@ -1095,7 +1136,18 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine, cairo_stroke (cr); } else - cairo_fill (cr); + { + if (alpha == 1) + cairo_fill (cr); + else + { + cairo_pattern_t *mask; + + mask = cairo_pattern_create_rgba (1, 1, 1, alpha); + cairo_mask (cr, mask); + cairo_pattern_destroy (mask); + } + } cairo_restore (cr); @@ -1544,8 +1596,6 @@ gtk_theming_engine_render_layout (GtkThemingEngine *engine, cairo_save (cr); flags = gtk_theming_engine_get_state (engine); - /* FIXME: Set clipping */ - gtk_theming_engine_get (engine, flags, "foreground-color", &fg_color, NULL); -- 2.30.2